深入理解java虚拟机 java内存区域与内存溢出异常

发现深入理解java虚拟机的读书笔记一直没发,一口气都发了好了。。。

概述

  • 运行时数据区
    • 所有线程共享的数据区
        • java虚拟机启动时创建
        • 所有对象的实例在这里分配内存
        • gc也在这里
      • 方法区
        • 储存已被虚拟机加载的类信息,常量,静态变量等数据
        • 运行池常量
    • 线程隔离的数据区
      • 程序计数器:当前线程所执行的字节码行号指示器、字节码解释器工作是通过改变该计数器值来选取下一条需要执行的字节码指令
      • java虚拟区栈:每个方法在执行的同时都会创建一个栈帧用于储存局部变量表,操作数栈、动态链接、方法出口等信息。每个方法执行到完成,就对应着一个栈帧在虚拟机栈中的入栈到出栈的过程
        • stackOverflowError:如果线程请求的栈深度大于虚拟机所允许的深度
        • OutOfMemroyError:虚拟机栈可以动态扩展,但是扩展式无法申请到足够内存
      • 本地方法栈:与java虚拟区栈类似,java虚拟区栈提供java方法服务,本地方法栈提供native方法服务
  • 执行引擎
  • 本地库接口
  • 直接内存:不是虚拟机的一部分,而是由native函数库直接分配的对外内存,然后通过一个java堆中的DirectByteBuffer对象作为该区域的引用来操作,在一些场景中可以提高性能

hotspot虚拟机对象探秘

对象的创建

  1. 检查这个指令的参数是否能在常量池中定位到一个类的符号引用。并且检查这个符号引用代表的类是否被加载解析和初始化。如果没有则需要进行类加载
  2. 为新生对象分配内存
    • 一种是所有线程不区分
    • 另一种是每个线程预先分配本地线程分配缓冲。可以使用-XX:+/-UserTLAB设置
  3. 将内存空间初始化为0
  4. 对对象进行设置:那个类的实例,如何找到,对象的哈希码,gc年龄信息等,存在对象头中
  5. 执行<init>方法

Java初始化顺序
jvm 初始化之 cinit, init

对象的内存布局

  • 对象头
    • 储存对象自身的运行时数据:hashcode,GC分代年龄,锁状态,线程持有的所,偏向线程id
    • 类型指针:指向他的雷元数据,通过这个指针确定是什么类的实例,另外如果是数组,还要记录长度
  • 示例数据:真正有效的数据,hotspot中相同长度的数据会分配到一起。父类一般在子类前面
  • 对齐填充

对象访问的定位

  1. 句柄池:优势,对象移动时reference稳定
    1
  2. 直接指针:优势,省空间
    2

OutOfMemoryError

  • java堆溢出
    • 不断创建对象并保证其不被垃圾回收即可
    • error message: java.lang.OutOfMemoryError: java heap space
    • 通过参数-XX:+HeapDumpOnOutOfMemoryError可以dump出内存转储快照,通过工具查看GC roots的引用链查看具体错误的类。
  • 虚拟机栈和本地方法栈溢出:定义大量本地变量,增加方法帧中的本地变量表长度或者递归调用次数过多。 大部分情况报的是StackOverflowError。
  • 方法区和运行时常量溢出:定义大量运行时常量,比如用string.intern生成存在常量池的string,并用-XX:Permsize, -XX:MaxPremSize限制方法区的大小。最后会报OutOfMemoryError:PremGen Space
  • 本机直接内存溢出。明显特征为Heap Dump中不会看见明显异常,而又直接或间接使用了NOI

本文采用创作共用保留署名-非商业-禁止演绎4.0国际许可证,欢迎转载,但转载请注明来自http://thousandhu.github.io,并保持转载后文章内容的完整。本人保留所有版权相关权利。

本文链接:http://thousandhu.github.io/2016/06/18/深入理解java虚拟机 java内存区域与内存溢出异常/